#include <gtest/gtest.h>
#include "scripting/script_lexer.h"
#include "scripting/script_parser.h"
#include "scripting/script_semantic_analyzer.h"
#include "types/reflex.h"
#include "../sample/docking_task.h"
#include "test_print_info.h"

namespace
{
bool analyzeASTNode(const script::ASTNode &ast_node)
{
    script::ScriptSemanticAnalyzer analyzer;
    if (analyzer.analyze(ast_node))
    {
        return true;
    }
    else
    {
        script::ErrorInfo error_info = analyzer.getErrorInfo();
        printf("%d:%d, analyze AST node fail, %s\n", error_info.line, error_info.column, error_info.error_message.c_str());
        return false;
    }
}
}

TEST(test_analyzer_scan_and_parser_var, test_analyzer)
{
    REGISTER_TASK_TYPE(DockingTask)

    const char *code = R"(var docking_task_2 = DockingTask("docking_task_2") #定义task)";

    script::ScriptLexer script_lexer;
    auto tokens = script_lexer.scan(code);
    if (tokens)
    {
        printf("code: %s\n", code);
        printTokens(tokens.value());
        script::ScriptParser script_parser;
        auto ast_node = script_parser.parse(tokens.value());
        if (ast_node)
        {
            printASTNode(ast_node.value());
            ASSERT_TRUE(analyzeASTNode(ast_node.value()));
        }
    }
    else
    {
        printErrorInfo(script_lexer);
        FAIL();
    }
}
TEST(test_analyzer_scan_and_parser_var_err, test_analyzer)
{
    const char *code = R"(
        var get_pose("get_pose*") #定义错误的task
    )";

    script::ScriptLexer script_lexer;
    auto tokens = script_lexer.scan(code);
    if (tokens)
    {
        printf("code: %s\n", code);
        printTokens(tokens.value());
        script::ScriptParser script_parser;
        auto ast_node = script_parser.parse(tokens.value());
        if (ast_node)
        {
            printASTNode(ast_node.value());
            ASSERT_TRUE(analyzeASTNode(ast_node.value()));
        }
        else
        {
            printASTError(script_parser);
            FAIL();
        }
    }
    else
    {
        printErrorInfo(script_lexer);
        SUCCEED();
    }
}

TEST(test_analyzer_scan_and_parser_run, test_analyzer)
{
    REGISTER_TASK_TYPE(DockingTask)

    const char *code = R"(
        var get_pose = DockingTask("get_pose")
        get_pose#执行task
    )";

    script::ScriptLexer script_lexer;
    auto tokens = script_lexer.scan(code);
    if (tokens)
    {
        printf("code: %s\n", code);
        printTokens(tokens.value());
        script::ScriptParser script_parser;
        auto ast_node = script_parser.parse(tokens.value());
        if (ast_node)
        {
            printASTNode(ast_node.value());
            ASSERT_TRUE(analyzeASTNode(ast_node.value()));
        }
        else
        {
            printASTError(script_parser);
            FAIL();
        }
    }
    else
    {
        printErrorInfo(script_lexer);
        FAIL();
    }
}
TEST(test_analyzer_scan_scan_if_sample, test_analyzer)
{
    const char *code = R"(if(!check_battery){navigation})";

    script::ScriptLexer script_lexer;
    auto tokens = script_lexer.scan(code);
    if (tokens)
    {
        printf("code: %s\n", code);
        printTokens(tokens.value());
        script::ScriptParser script_parser;
        auto ast_node = script_parser.parse(tokens.value());
        if (ast_node)
        {
            printASTNode(ast_node.value());
            ASSERT_TRUE(analyzeASTNode(ast_node.value()));
        }
        else
        {
            printASTError(script_parser);
            FAIL();
        }
    }
    else
    {
        printErrorInfo(script_lexer);
        FAIL();
    }
}
TEST(test_analyzer_scan_scan_if, test_analyzer)
{
    const char *code = R"(
        if(check_battery and !get_pose)
        {
            navigation#运行导航
        }
        else
        {
            docking
        }
    )";

    script::ScriptLexer script_lexer;
    auto tokens = script_lexer.scan(code);
    if (tokens)
    {
        printf("code: %s\n", code);
        printTokens(tokens.value());
        script::ScriptParser script_parser;
        auto ast_node = script_parser.parse(tokens.value());
        if (ast_node)
        {
            printASTNode(ast_node.value());
            ASSERT_TRUE(analyzeASTNode(ast_node.value()));
        }
        else
        {
            printASTError(script_parser);
            FAIL();
        }
    }
    else
    {
        printErrorInfo(script_lexer);
        FAIL();
    }
}
TEST(test_analyzer_scan_scan_if_nest, test_analyzer)
{
    const char *code = R"(
        if(check_battery or !(get_pose and check_pose))
        {
            navigation#运行导航
        }
        else
        {
            docking
        }
    )";

    script::ScriptLexer script_lexer;
    auto tokens = script_lexer.scan(code);
    if (tokens)
    {
        printf("code: %s\n", code);
        printTokens(tokens.value());
        script::ScriptParser script_parser;
        auto ast_node = script_parser.parse(tokens.value());
        if (ast_node)
        {
            printASTNode(ast_node.value());
            ASSERT_TRUE(analyzeASTNode(ast_node.value()));
        }
        else
        {
            printASTError(script_parser);
            FAIL();
        }
    }
    else
    {
        printErrorInfo(script_lexer);
        FAIL();
    }
}
TEST(test_script_parse_repeat, test_analyzer)
{
    const char *code = R"(
        #repeat
        repeat(3)
        {
            navigation
        }
    )";

    script::ScriptLexer script_lexer;
    auto tokens = script_lexer.scan(code);
    if (tokens)
    {
        printf("code: %s\n", code);
        printTokens(tokens.value());
        script::ScriptParser script_parser;
        auto ast_node = script_parser.parse(tokens.value());
        if (ast_node)
        {
            printASTNode(ast_node.value());
            ASSERT_TRUE(analyzeASTNode(ast_node.value()));
        }
        else
        {
            printASTError(script_parser);
            FAIL();
        }
    }
    else
    {
        printErrorInfo(script_lexer);
        FAIL();
    }
}
TEST(test_script_parse_retry, test_analyzer)
{
    const char *code = R"(
        #repeat
        retry(3)
        {
            navigation
        }
    )";

    script::ScriptLexer script_lexer;
    auto tokens = script_lexer.scan(code);
    if (tokens)
    {
        printf("code: %s\n", code);
        printTokens(tokens.value());
        script::ScriptParser script_parser;
        auto ast_node = script_parser.parse(tokens.value());
        if (ast_node)
        {
            printASTNode(ast_node.value());
            ASSERT_TRUE(analyzeASTNode(ast_node.value()));
        }
        else
        {
            printASTError(script_parser);
            FAIL();
        }
    }
    else
    {
        printErrorInfo(script_lexer);
        FAIL();
    }
}
TEST(test_script_parse_while_break, test_analyzer)
{
    const char *code = R"(
        #while
        while(arrived)
        {
            # sleep(1000)
            if(navigation)
            {
                break
            }
        }
    )";

    script::ScriptLexer script_lexer;
    auto tokens = script_lexer.scan(code);
    if (tokens)
    {
        printf("code: %s\n", code);
        printTokens(tokens.value());
        script::ScriptParser script_parser;
        auto ast_node = script_parser.parse(tokens.value());
        if (ast_node)
        {
            printASTNode(ast_node.value());
            ASSERT_TRUE(analyzeASTNode(ast_node.value()));
        }
        else
        {
            printASTError(script_parser);
            FAIL();
        }
    }
    else
    {
        printErrorInfo(script_lexer);
        FAIL();
    }
}
TEST(test_script_parse_while, test_analyzer)
{
    const char *code = R"(
        #while
        while(arrived)
        {
            navigation
        }
    )";

    script::ScriptLexer script_lexer;
    auto tokens = script_lexer.scan(code);
    if (tokens)
    {
        printf("code: %s\n", code);
        printTokens(tokens.value());
        script::ScriptParser script_parser;
        auto ast_node = script_parser.parse(tokens.value());
        if (ast_node)
        {
            printASTNode(ast_node.value());
            ASSERT_TRUE(analyzeASTNode(ast_node.value()));
        }
        else
        {
            printASTError(script_parser);
            FAIL();
        }
    }
    else
    {
        printErrorInfo(script_lexer);
        FAIL();
    }
}
TEST(test_script_parse_while_no_executant, test_analyzer)
{
    const char *code = R"(
        #while
        while(arrived)
        {
        }
    )";

    script::ScriptLexer script_lexer;
    auto tokens = script_lexer.scan(code);
    if (tokens)
    {
        printf("code: %s\n", code);
        printTokens(tokens.value());
        script::ScriptParser script_parser;
        auto ast_node = script_parser.parse(tokens.value());
        if (ast_node)
        {
            printASTNode(ast_node.value());
            ASSERT_TRUE(analyzeASTNode(ast_node.value()));
        }
        else
        {
            printASTError(script_parser);
            FAIL();
        }
    }
    else
    {
        printErrorInfo(script_lexer);
        FAIL();
    }
}
TEST(test_script_parse_reactive, test_analyzer)
{
    const char *code = R"(
        #reactive
        reactive(check_battery){
            navigation
        }
    )";

    script::ScriptLexer script_lexer;
    auto tokens = script_lexer.scan(code);
    if (tokens)
    {
        printf("code: %s\n", code);
        printTokens(tokens.value());
        script::ScriptParser script_parser;
        auto ast_node = script_parser.parse(tokens.value());
        if (ast_node)
        {
            printASTNode(ast_node.value());
            ASSERT_TRUE(analyzeASTNode(ast_node.value()));
        }
        else
        {
            printASTError(script_parser);
            FAIL();
        }
    }
    else
    {
        printErrorInfo(script_lexer);
        FAIL();
    }
}
TEST(test_script_parse_parallel, test_analyzer)
{
    const char *code = R"(
        #parallel
        parallel
        {
            navigation
            docking
            recharge
        }
    )";

    script::ScriptLexer script_lexer;
    auto tokens = script_lexer.scan(code);
    if (tokens)
    {
        printf("code: %s\n", code);
        printTokens(tokens.value());
        script::ScriptParser script_parser;
        auto ast_node = script_parser.parse(tokens.value());
        if (ast_node)
        {
            printASTNode(ast_node.value());
            ASSERT_TRUE(analyzeASTNode(ast_node.value()));
        }
        else
        {
            printASTError(script_parser);
            FAIL();
        }
    }
    else
    {
        printErrorInfo(script_lexer);
        FAIL();
    }
}
